#include <QDebug>
#include <QApplication>
#include <QDesktopWidget>
#include <QGridLayout>
#include <QX11Info>

#include "dexwin.h"
#include "dextitlebar.h"
#include "cwframe.h"

DexWin::DexWin(Window w, QWidget *parent, Qt::WindowFlags f)
	: QFrame(parent, f)
	{
		setWindowFlags(Qt::Window);
		clientId = w;

		setStyleSheet("background-color: qlineargradient(x1:0, y1:1, x2:0, y2:0, stop:0 white, stop: 0.4 #AAAAAA, stop:0.45 #CCCCCC, stop: 1 white);");

		setFrameShape(QFrame::Box);
		setLineWidth(1);

		XGrabServer(QX11Info::display());

		constructFrames();
		/// Implement style system here
		updateWMName();
		getWMSize();

//		XAddToSaveSet(QX11Info::display(), clientId);
		XReparentWindow(QX11Info::display(), clientId, clientWidget->winId(), 0, 0);
		XMapWindow(QX11Info::display(), clientId);
		// For desktop panels...
		sendClientMessage(XInternAtom(QX11Info::display(), "_NET_ACTIVE_WINDOW", false),
						  QApplication::desktop()->winId(),
						  1,
						  StructureNotifyMask);
		QApplication::syncX();

		XUngrabServer(QX11Info::display());

		show();
	}

DexWin::~DexWin()
	{
//		delete ui;
	}

void DexWin::closeWindow()
	{
		sendClientMessage(XInternAtom(QX11Info::display(), "WM_PROTOCOLS", False),
						  clientId,
						  XInternAtom(QX11Info::display(), "WM_DELETE_WINDOW", FALSE),
						  0L);
	}

void DexWin::constructFrames()
	{
		layout = new QGridLayout(this);
		layout->setMargin(0);
		layout->setSpacing(0);

		titleBar = new DexTitleBar(this);
		titleBar->setSizePolicy(QSizePolicy::MinimumExpanding,
								QSizePolicy::Maximum);
		titleBar->setStyleSheet("QFrame#titleBar {"
								"	max-height: 30px;"
								"}"
								"QPushButton {"
								"	min-width: 10px;"
								"	max-width: 13px;"
								"	min-height: 10px;"
								"	max-height: 13px !important;"
								"	border: 1px solid white;"
								"	background-color: black;"
//								"	background-color: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:1.4, fx:0.5, fy:0.5, stop:0 rgba(200, 200, 200, 255), stop:0.5 rgba(0, 0, 0, 255));"
								"	border-radius: 4px;"
								"}");
		titleBar->setObjectName("titleBar");
		layout->addWidget(titleBar, 0, 1);
		connect(titleBar, SIGNAL(closeWindow()), this, SLOT(closeWindow()));
		connect(titleBar, SIGNAL(moveWindow(QPoint,QPoint)), this, SLOT(moveWindow(QPoint,QPoint)));

		frameTop = new CWFrame(Qt::TopSection, titleBar);

		frameTopLeft = new CWFrame(Qt::TopLeftSection, this);
		layout->addWidget(frameTopLeft, 0, 0);

		frameTopRight = new CWFrame(Qt::TopRightSection, this);
		layout->addWidget(frameTopRight, 0, 2);

		clientWidget = new QFrame(this);
		clientWidget->setSizePolicy(QSizePolicy::MinimumExpanding,
									QSizePolicy::MinimumExpanding);
		clientWidget->setObjectName("clientWidget");
		clientWidget->setStyleSheet("background-color: white;");
		layout->addWidget(clientWidget, 1, 1);
		XSelectInput(QX11Info::display(), clientWidget->winId(), KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask |
					 KeymapStateMask | ButtonMotionMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask | FocusChangeMask |
					 VisibilityChangeMask | ExposureMask | StructureNotifyMask | SubstructureRedirectMask | SubstructureNotifyMask);
		// Required for name changes.
		XSetWindowAttributes at;
		at.event_mask = PropertyChangeMask;
		XChangeWindowAttributes(QX11Info::display(), clientId, CWEventMask, &at);

		frameLeft = new CWFrame(Qt::LeftSection, this);
		layout->addWidget(frameLeft, 1, 0);

		frameRight = new CWFrame(Qt::RightSection, this);
		layout->addWidget(frameRight, 1, 2);

		frameBottom = new CWFrame(Qt::BottomSection, this);
		layout->addWidget(frameBottom, 2, 1);

		frameBottomLeft = new CWFrame(Qt::BottomLeftSection, this);
		layout->addWidget(frameBottomLeft, 2, 0);

		frameBottomRight = new CWFrame(Qt::BottomRightSection, this);
		layout->addWidget(frameBottomRight, 2, 2);
	}

QFrame *DexWin::getFrameSection(Qt::WindowFrameSection sect)
{
	switch (sect)
	{
		case Qt::TopSection:
			return frameTop;
		case Qt::TopLeftSection:
			return frameTopLeft;
		case Qt::TopRightSection:
			return frameTopRight;
		case Qt::BottomSection:
			return frameBottom;
		case Qt::BottomLeftSection:
			return frameBottomLeft;
		case Qt::BottomRightSection:
			return frameBottomRight;
		case Qt::LeftSection:
			return frameLeft;
		case Qt::RightSection:
			return frameRight;
		case Qt::TitleBarArea:
			return titleBar;
		case Qt::NoSection:
			break;
	}
	return this;
}

void DexWin::getWMSize()
	{
		XSizeHints sHints;
		long supp;
		if (XGetWMNormalHints(QX11Info::display(),
							  clientId,
							  &sHints,
							  &supp))
		{
			hintMinWidth = sHints.min_width;
			hintMinHeight = sHints.min_height;
			hintMaxWidth = sHints.max_width;
			hintMaxHeight = sHints.max_height;
			hintBaseWidth = sHints.base_width;
			hintBaseHeight = sHints.base_height;
			hintIncWidth = sHints.width_inc;
			hintIncHeight = sHints.height_inc;
			hintMinX = sHints.min_aspect.x;
			hintMinY = sHints.min_aspect.y;
			hintMaxX = sHints.max_aspect.x;
			hintMaxY = sHints.max_aspect.y;
		}

		// In case the one above fails...
		else if (XGetSizeHints(QX11Info::display(),
							   clientId,
							   &sHints,
							   XA_WM_NORMAL_HINTS))
		{
			hintMinWidth = sHints.min_width;
			hintMinHeight = sHints.min_height;
			hintMaxWidth = sHints.max_width;
			hintMaxHeight = sHints.max_height;
			hintBaseWidth = sHints.base_width;
			hintBaseHeight = sHints.base_height;
			hintIncWidth = sHints.width_inc;
			hintIncHeight = sHints.height_inc;
			hintMinX = sHints.min_aspect.x;
			hintMinY = sHints.min_aspect.y;
			hintMaxX = sHints.max_aspect.x;
			hintMaxY = sHints.max_aspect.y;
		}

		// All because the above methods don't help all that much
		//	for obtaining the default width and height.
		XWindowAttributes attr;
		if (XGetWindowAttributes(QX11Info::display(), clientId, &attr))
		{
			m_width = attr.width;
			m_height = attr.height;
			m_x = attr.x;
			m_y = attr.y;

			/** \note
			 *
			 *	Some clients give a width and height that does not
			 *	 faithfully represent the client's visual size.
			 */

//			qDebug() << "... Base" << hintBaseWidth << "x" << hintBaseHeight;
//			qDebug() << "... Min" << hintMinWidth << "x" << hintMinHeight;
//			qDebug() << "... Max" << hintMaxWidth << "x" << hintMaxHeight;
//			qDebug() << "..." << m_width << "x" << m_height;

			// Can't be having them 0 values...
			if (hintMinWidth > 0 && hintMinHeight > 0)
			{
				// But also can't be having sizes lower than minimum
				if (m_width > hintMinWidth)
					setMinimumWidth(frameLeft->frameGeometry().width() * 2 + hintMinWidth);
				if (m_height > hintMinHeight)
					setMinimumHeight(frameTopLeft->frameGeometry().height() + frameBottom->frameGeometry().height() + hintMinHeight);
			}
			else // Give it something small
				setMinimumSize(35, 35);

			if (hintMaxWidth > 0 && hintMaxHeight > 0)
				setMaximumSize(frameLeft->frameGeometry().width() * 2 + hintMaxWidth,
							   frameTopLeft->frameGeometry().height() + frameBottom->frameGeometry().height() + hintMaxHeight);

			resize(frameLeft->frameGeometry().width() * 2 + m_width,
				   frameTopLeft->frameGeometry().height() + frameBottom->frameGeometry().height() + m_height);
		}
	}

void DexWin::moveWindow(const QPoint &oldPos, const QPoint &pos)
	{
		if (windowState() != Qt::WindowMaximized)
		{
			int x = (oldPos.x() > pos.x()) ? geometry().x()-(oldPos.x()-pos.x()) : geometry().x()+(pos.x()-oldPos.x());
			int y = (oldPos.x() > pos.y()) ? geometry().y()-(oldPos.y()-pos.y()) : geometry().y()+(pos.y()-oldPos.y());

			// Prevent X and Y from going too far off the screen (i.e. top and left).
			if (x < -50) x = -50;
			if (y < 1) y = 0;

			// Prevent X and Y from going too far off the screen (i.e. bottom and right).
			int dw = QApplication::desktop()->width();
			int dh = QApplication::desktop()->height();
			if (x > dw-100) x = dw-100;
			if (y > dh-100) y = dh-100;

			move(x, y);
		}
	}

void DexWin::sendClientMessage(Atom messageType, Window win, long data, long eventMask)
	{
		Display *dpy = QX11Info::display();
		XEvent event;
		memset(&event, 0, sizeof(event));
		event.type = ClientMessage;
		event.xclient.type = ClientMessage;
		event.xclient.display = dpy;
		event.xclient.window = clientId;
		event.xclient.message_type = messageType;
		event.xclient.format = 32;
		event.xclient.data.l[0] = data;
		XSendEvent(dpy, win, false, eventMask, &event);
	}

void DexWin::setWindowTitle(const QString &title)
	{
		QFrame::setWindowTitle(title);
		titleBar->setWindowTitle(title);
		titleBar->update();
	}

void DexWin::updateWMName()
	{
		char *wName;
		XFetchName(QX11Info::display(), clientId, &wName);
		setWindowTitle(QString(wName));
		qDebug() << windowTitle();
		XFree(wName); // free up memory taken by wName.
	}
